package gu.sql2java.transformer;

import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashSet;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.base.Supplier;
import com.google.common.collect.FluentIterable;
import com.google.common.reflect.TypeToken;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkArgument;

/**
 * {@link TransformerContext} 的容器类，用于存储 {@link TransformerContext} 对象。 实现
 * {@link ContextProvider}接口，用于提供 {@link TransformerContext.LocalContext} 对象。
 * 
 * @author guyadong
 * @since 4.4.0
 */
public class ContextContainer extends TransformerContext {
	/**
	 * 有效 {@link ContextProvider} 对象过滤器
	 */
	private static final Predicate<ContextProvider> VALID_CONTEXTS_FILTER = new Predicate<ContextProvider>() {
		@Override
		public boolean apply(ContextProvider input) {
			return null != input && input.getContext() != null && input.getContext().isOverriden();
		}
	};
	/**
	 * 默认 {@link ContextContainer} 对象
	 */
	public static final TransformerContext DEFAULT_CONTAINER = new ContextContainer();

	/**
	 * 存储 {@link ContextProvider} 对象的集合, 优先级从左到右递减
	 */
	private final LinkedHashSet<ContextProvider> contexts = new LinkedHashSet<>();

    /**
	 * 构造函数
	 */
	public ContextContainer(ContextProvider... providers) {
		FluentIterable.from(checkNotNull(providers, "providers is null")).filter(VALID_CONTEXTS_FILTER)
				.copyInto(contexts);
	}

	public ContextContainer(Iterable<ContextProvider> providers) {
		FluentIterable.from(checkNotNull(providers, "providers is null")).filter(VALID_CONTEXTS_FILTER)
				.copyInto(contexts);
	}

	/**
	 * 从 {@link #contexts}中顺序查找 {@link TransformerContext} 对象，并调用
	 * {@link #placeholderOnNull(BaseTypeTransformer, TypeToken)} 方法。
	 * 遇到第一个非空结果则返回，否则返回 {@code null}。
	 */
	@Override
	public final <T> Supplier<T> placeholderOnNull(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
		for (ContextProvider context : contexts) {
			Supplier<T> result = context.getContext().placeholderOnNull(typeTransformer, valueType);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	/**
	 * 从 {@link #contexts}中顺序查找 {@link TransformerContext} 对象，并调用
	 * {@link #placeholderOnEmpty(BaseTypeTransformer, TypeToken)} 方法。
	 * 遇到第一个非空结果则返回，否则返回 {@code null}。
	 */
	@Override
	public final <T> Supplier<T> placeholderOnEmpty(BaseTypeTransformer typeTransformer, TypeToken<T> valueType) {
		for (ContextProvider context : contexts) {
			Supplier<T> result = context.getContext().placeholderOnEmpty(typeTransformer, valueType);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	/**
	 * 从 {@link #contexts}中顺序查找 {@link TransformerContext} 对象，并调用
	 * {@link #elementFilter(BaseTypeTransformer, TypeToken)} 方法。 将所有非空结果合并为一个
	 * {@link Predicate}，并返回。 如果过滤器集合为空，则返回 {@code null}。
	 */
	@SuppressWarnings("unchecked")
	@Override
	public final <T, E> Predicate<T> elementFilter(final BaseTypeTransformer typeTransformer, final TypeToken<E> elementType) {
		FluentIterable<Predicate<Object>> filters = FluentIterable.from(contexts)
				.transform(new Function<ContextProvider, Predicate<Object>>() {
					@Override
					public Predicate<Object> apply(ContextProvider ctx) {
						return ctx.getContext().elementFilter(typeTransformer, elementType);
					}
				}).filter(Predicates.notNull());
		return filters.isEmpty() ? null : (Predicate<T>)Predicates.and(filters);
	}	
	
	/**
	 * 从 {@link #contexts}中顺序查找 {@link TransformerContext} 对象，并调用
	 * {@link #functionOnTransformError(BaseTypeTransformer, TypeToken)} 方法。
	 * 遇到第一个非空结果则返回，否则返回 {@code null}。
	 */
	@Override
	public <T, E extends Throwable> OnTransformError<T, E> functionOnTransformError(BaseTypeTransformer typeTransformer,
			TypeToken<T> valueType) {
		for (ContextProvider context : contexts) {
			OnTransformError<T, E> result = context.getContext().functionOnTransformError(typeTransformer, valueType);
			if (result != null) {
				return result;
			}
		}
		return null;
	}

	/**
	 * 获取 {@link #contexts} 的大小。ONLY FOR TEST.
	 */
	int size() {
		return contexts.size();
	}
	/**
	 * 判断 {@link #contexts} 是否包含 {@code o}。ONLY FOR TEST.
	 * @param o
	 * @return
	 */
	boolean contains(Object o) {
		return contexts.contains(o);
	}
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("ContextContainer [contexts=").append(contexts).append("]");
		return builder.toString();
	}

	/**
	 * 合并 {@code providers} 对象添加到当前 {@link ContextContainer} 对象的
	 * {@link #contexts} 的前面(优先级更高)，并返回新的 {@link ContextContainer} 对象。 
	 * 
	 * @param providers {@link ContextProvider} 对象，优先级从左到右递减
	 * @return 新的 {@link ContextContainer} 对象或当前对象
	 * @see #merge(ContextProvider, Iterable)
	 */
	public ContextContainer with(ContextProvider... providers) {
		return (ContextContainer) merge(this, providers);
	}

	/**
	 * 合并 {@code providers} 对象添加到当前 {@link ContextContainer} 对象的
	 * {@link #contexts} 的前面(优先级更高)，并返回新的 {@link ContextContainer} 对象。 
	 * 
	 * @param providers {@link ContextProvider} 对象，优先级从左到右递减
	 * @return 新的 {@link ContextContainer} 对象或当前对象
	 * @see #merge(ContextProvider, Iterable)
	 */
	public ContextContainer with(Iterable<ContextProvider> providers) {
		return (ContextContainer) merge(this, providers);
	}
	/**
	 * @see #merge(ContextProvider, Iterable)
	 */
	public static ContextProvider merge(ContextProvider other,ContextProvider... providers) {
		return merge(other, Arrays.asList(null == providers ? new ContextProvider[0] : providers));
	}
	
	/**
	 * 合并一组 {@code providers} 对象到 {@code other}，并返回新的 {@link ContextContainer} 对象。
	 * 如果 {@code providers} 没有提供有效的 {@link ContextProvider} 对象，则返回 {@code other}。 如果
	 * {@code other} 是 {@link ContextContainer} 对象，则将 {@code providers} 添加到
	 * {@link #contexts}的前面(优先级更高)， 并返回新的 {@link ContextContainer} 对象。 否则 将
	 * {@code other} 添加到 {@code providers} 之后，并返回新的 {@link ContextContainer} 对象。
	 * 
	 * @param other     {@link ContextProvider} 对象
	 * @param providers {@link ContextProvider} 对象，优先级从左到右递减
	 * @return 新的 {@link ContextContainer} 对象或 {@code other}
	 */
	public static ContextProvider merge(final ContextProvider other,Iterable<ContextProvider> providers) {
		checkArgument(other != null, "other is null");
		LinkedHashSet<ContextProvider> providerset = FluentIterable.from(null == providers ? Collections.<ContextProvider>emptyList() : providers)
				.filter(VALID_CONTEXTS_FILTER).filter(new Predicate<ContextProvider>() {
					@Override
					public boolean apply(ContextProvider input) {
						return input != other && input.getContext() != other;
					}
				}).copyInto(new LinkedHashSet<ContextProvider>());
		if (providerset.isEmpty()) {
			return other;
		}
		if(other instanceof ContextContainer) {
			providerset.addAll(((ContextContainer)other).contexts);
			return new ContextContainer(providerset);
		}else if(!other.getContext().isOverriden() && providerset.size() == 1){
			return providerset.iterator().next();
		} else {
			return new ContextContainer(FluentIterable.from(providerset).append(other));
		}
	}
}
